//! 重试机制实现

use std::time::Duration;
use tokio::time::sleep;
use tracing::warn;

use crate::error::ClientError;
use crate::config::ClientConfig;

/// 重试策略枚举
#[derive(Debug, Clone)]
pub enum RetryStrategy {
    ExponentialBackoff(ExponentialBackoffRetry),
    FixedInterval(FixedIntervalRetry),
    NoRetry(NoRetry),
}

impl RetryStrategy {
    pub async fn execute<F, Fut, R>(&self, mut operation: F) -> Result<R, ClientError>
    where
        F: FnMut() -> Fut + Send + Sync,
        Fut: std::future::Future<Output = Result<R, ClientError>> + Send,
        R: Send + std::marker::Send,
    {
        match self {
            RetryStrategy::ExponentialBackoff(retry) => retry.execute(operation).await,
            RetryStrategy::FixedInterval(retry) => retry.execute(operation).await,
            RetryStrategy::NoRetry(retry) => retry.execute(operation).await,
        }
    }

    pub fn max_retries(&self) -> u32 {
        match self {
            RetryStrategy::ExponentialBackoff(retry) => retry.max_retries,
            RetryStrategy::FixedInterval(retry) => retry.max_retries,
            RetryStrategy::NoRetry(_) => 0,
        }
    }
}

/// 指数退避重试策略
#[derive(Debug, Clone)]
pub struct ExponentialBackoffRetry {
    max_retries: u32,
    initial_interval: Duration,
    max_interval: Duration,
    multiplier: f64,
}

impl ExponentialBackoffRetry {
    pub fn new(max_retries: u32, initial_interval: Duration) -> Self {
        Self {
            max_retries,
            initial_interval,
            max_interval: Duration::from_secs(30),
            multiplier: 2.0,
        }
    }

    pub fn with_max_interval(mut self, max_interval: Duration) -> Self {
        self.max_interval = max_interval;
        self
    }

    pub fn with_multiplier(mut self, multiplier: f64) -> Self {
        self.multiplier = multiplier;
        self
    }
}

impl ExponentialBackoffRetry {
    pub async fn execute<F, Fut, R>(&self, mut operation: F) -> Result<R, ClientError>
    where
        F: FnMut() -> Fut + Send + Sync,
        Fut: std::future::Future<Output = Result<R, ClientError>> + Send,
        R: Send + std::marker::Send,
    {
        let mut retries = 0;
        let mut interval = self.initial_interval;

        loop {
            match operation().await {
                Ok(result) => return Ok(result),
                Err(e) if retries < self.max_retries => {
                    retries += 1;
                    warn!("Retry attempt {} after error: {}", retries, e);
                    
                    sleep(interval).await;
                    
                    interval = std::cmp::min(
                        Duration::from_millis((interval.as_millis() as f64 * self.multiplier) as u64),
                        self.max_interval,
                    );
                }
                Err(e) => {
                    return Err(ClientError::RetryExhausted(format!(
                        "Max retries ({}) exhausted. Last error: {}",
                        self.max_retries, e
                    )));
                }
            }
        }
    }
}

/// 固定间隔重试策略
#[derive(Debug, Clone)]
pub struct FixedIntervalRetry {
    max_retries: u32,
    interval: Duration,
}

impl FixedIntervalRetry {
    pub fn new(max_retries: u32, interval: Duration) -> Self {
        Self { max_retries, interval }
    }
}

impl FixedIntervalRetry {
    pub async fn execute<F, Fut, R>(&self, mut operation: F) -> Result<R, ClientError>
    where
        F: FnMut() -> Fut + Send + Sync,
        Fut: std::future::Future<Output = Result<R, ClientError>> + Send,
        R: Send + std::marker::Send,
    {
        let mut retries = 0;

        loop {
            match operation().await {
                Ok(result) => return Ok(result),
                Err(e) if retries < self.max_retries => {
                    retries += 1;
                    warn!("Retry attempt {} after error: {}", retries, e);
                    
                    sleep(self.interval).await;
                }
                Err(e) => {
                    return Err(ClientError::RetryExhausted(format!(
                        "Max retries ({}) exhausted. Last error: {}",
                        self.max_retries, e
                    )));
                }
            }
        }
    }
}

/// 无重试策略
#[derive(Debug, Clone)]
pub struct NoRetry;

impl NoRetry {
    pub async fn execute<F, Fut, R>(
        &self,
        mut operation: F,
    ) -> Result<R, ClientError>
    where
        F: FnMut() -> Fut + Send + Sync,
        Fut: std::future::Future<Output = Result<R, ClientError>> + Send,
        R: Send,
    {
        operation().await
    }
}

/// 重试策略工厂
pub struct RetryStrategyFactory;

impl RetryStrategyFactory {
    pub fn create(config: &ClientConfig) -> RetryStrategy {
        match config.max_retries {
            0 => RetryStrategy::NoRetry(NoRetry),
            retries => RetryStrategy::ExponentialBackoff(ExponentialBackoffRetry::new(
                retries,
                config.retry_interval_duration(),
            )),
        }
    }
}

/// 可重试的操作包装器
pub struct Retryable<F, Fut, R> {
    operation: F,
    strategy: RetryStrategy,
    _phantom: std::marker::PhantomData<(Fut, R)>,
}

impl<F, Fut, R> Retryable<F, Fut, R>
where
    F: FnMut() -> Fut + Send + Sync,
    Fut: std::future::Future<Output = Result<R, ClientError>> + Send,
    R: Send + Sync,
{
    pub fn new(operation: F, strategy: RetryStrategy) -> Self {
        Self {
            operation,
            strategy,
            _phantom: std::marker::PhantomData,
        }
    }

    pub async fn execute(self) -> Result<R, ClientError> {
        self.strategy.execute(self.operation).await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tokio::time::sleep;

    #[tokio::test]
    async fn test_exponential_backoff_retry() {
        let retry = ExponentialBackoffRetry::new(
            3,
            Duration::from_millis(10),
        );

        let mut attempts = 0;
        let result = retry
            .execute(|| {
                attempts += 1;
                async move {
                    if attempts < 3 {
                        Err(ClientError::NetworkError("test".to_string()))
                    } else {
                        Ok(())
                    }
                }
            })
            .await;

        assert!(result.is_ok());
        assert_eq!(attempts, 3);
    }

    #[tokio::test]
    async fn test_fixed_interval_retry() {
        let retry = FixedIntervalRetry::new(
            2,
            Duration::from_millis(10),
        );

        let mut attempts = 0;
        let result = retry
            .execute(|| {
                attempts += 1;
                async move {
                    if attempts < 2 {
                        Err(ClientError::NetworkError("test".to_string()))
                    } else {
                        Ok(())
                    }
                }
            })
            .await;

        assert!(result.is_ok());
        assert_eq!(attempts, 2);
    }

    #[tokio::test]
    async fn test_no_retry() {
        let retry = NoRetry;

        let mut attempts = 0;
        let result = retry
            .execute(|| {
                attempts += 1;
                async move {
                    Err(ClientError::NetworkError("test".to_string()))
                }
            })
            .await;

        assert!(result.is_err());
        assert_eq!(attempts, 1);
    }

    #[tokio::test]
    async fn test_retry_exhausted() {
        let retry = ExponentialBackoffRetry::new(
            2,
            Duration::from_millis(10),
        );

        let result = retry
            .execute(|| async move {
                Err(ClientError::NetworkError("test".to_string()))
            })
            .await;

        assert!(matches!(result, Err(ClientError::RetryExhausted(_))));
    }

    #[tokio::test]
    async fn test_retry_strategy_factory() {
        let config = ClientConfig {
            max_retries: 3,
            retry_interval: 100,
            ..Default::default()
        };

        let strategy = RetryStrategyFactory::create(&config);
        assert_eq!(strategy.max_retries, 3);
    }

    #[tokio::test]
    async fn test_retryable_wrapper() {
        let strategy = RetryStrategy::FixedInterval(FixedIntervalRetry::new(2, Duration::from_millis(10)));
        
        let mut attempts = 0;
        let retryable = Retryable::new(
            || {
                attempts += 1;
                async move {
                    if attempts < 2 {
                        Err(ClientError::NetworkError("test".to_string()))
                    } else {
                        Ok(())
                    }
                }
            },
            strategy,
        );

        let result = retryable.execute().await;
        assert!(result.is_ok());
        assert_eq!(attempts, 2);
    }

    #[tokio::test]
    async fn test_retry_with_success() {
        let retry = ExponentialBackoffRetry::new(
            3,
            Duration::from_millis(10),
        );

        let result = retry
            .execute(|| async move { Ok(42) })
            .await;

        assert_eq!(result.unwrap(), 42);
    }

    #[tokio::test]
    async fn test_retry_with_immediate_success() {
        let retry = ExponentialBackoffRetry::new(
            3,
            Duration::from_millis(10),
        );

        let mut attempts = 0;
        let result = retry
            .execute(|| {
                attempts += 1;
                async move { Ok(attempts) }
            })
            .await;

        assert_eq!(result.unwrap(), 1);
        assert_eq!(attempts, 1);
    }

    #[tokio::test]
    async fn test_retry_backoff_timing() {
        let retry = ExponentialBackoffRetry::new(
            3,
            Duration::from_millis(10),
        );

        let start = std::time::Instant::now();
        let _ = retry
            .execute(|| async move {
                Err(ClientError::NetworkError("test".to_string()))
            })
            .await;

        let elapsed = start.elapsed();
        assert!(elapsed >= Duration::from_millis(10));
    }

    #[tokio::test]
    async fn test_retry_max_interval() {
        let retry = ExponentialBackoffRetry::new(
            5,
            Duration::from_millis(10),
        )
        .with_max_interval(Duration::from_millis(50));

        let mut attempts = 0;
        let result = retry
            .execute(|| {
                attempts += 1;
                async move {
                    if attempts < 5 {
                        Err(ClientError::NetworkError("test".to_string()))
                    } else {
                        Ok(())
                    }
                }
            })
            .await;

        assert!(result.is_ok());
        assert_eq!(attempts, 5);
    }

    #[tokio::test]
    async fn test_retry_multiplier() {
        let retry = ExponentialBackoffRetry::new(
            4,
            Duration::from_millis(10),
        )
        .with_multiplier(1.5);

        let mut attempts = 0;
        let result = retry
            .execute(|| {
                attempts += 1;
                async move {
                    if attempts < 4 {
                        Err(ClientError::NetworkError("test".to_string()))
                    } else {
                        Ok(())
                    }
                }
            })
            .await;

        assert!(result.is_ok());
        assert_eq!(attempts, 4);
    }

    #[tokio::test]
    async fn test_retry_strategy_factory_no_retry() {
        let config = ClientConfig {
            max_retries: 0,
            ..Default::default()
        };

        let strategy = RetryStrategyFactory::create(&config);
        
        let result = strategy
            .execute(|| async move {
                Err(ClientError::NetworkError("test".to_string()))
            })
            .await;

        assert!(matches!(result, Err(ClientError::NetworkError(_))));
    }

    #[tokio::test]
    async fn test_retry_with_different_error_types() {
        let retry = FixedIntervalRetry::new(2, Duration::from_millis(10));

        let mut attempts = 0;
        let result = retry
            .execute(|| {
                attempts += 1;
                async move {
                    match attempts {
                        1 => Err(ClientError::NetworkError("network".to_string())),
                        2 => Err(ClientError::ServerError("server".to_string())),
                        _ => Ok(()),
                    }
                }
            })
            .await;

        assert!(result.is_err());
        assert_eq!(attempts, 2);
    }

    #[tokio::test]
    async fn test_retry_concurrent_access() {
        use std::sync::Arc;
        use tokio::sync::Mutex;

        let retry = Arc::new(FixedIntervalRetry::new(3, Duration::from_millis(10)));
        let counter = Arc::new(Mutex::new(0));

        let mut handles = vec![];
        for _ in 0..5 {
            let retry = retry.clone();
            let counter = counter.clone();
            let handle = tokio::spawn(async move {
                let result = retry
                    .execute(|| {
                        let counter = counter.clone();
                        async move {
                            let mut count = counter.lock().await;
                            *count += 1;
                            if *count < 3 {
                                Err(ClientError::NetworkError("test".to_string()))
                            } else {
                                Ok(*count)
                            }
                        }
                    })
                    .await;
                result
            });
            handles.push(handle);
        }

        let results = futures::future::join_all(handles).await;
        let success_count = results.iter().filter(|r| r.is_ok()).count();
        assert!(success_count > 0);
    }
}